﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using Emgu.CV;
using Emgu.CV.Structure;
using Newtonsoft.Json;

namespace BKBIS
{
    /// <summary>
    /// Lớp này chứa các hàm dùng cho quá trình training
    /// </summary>
    class Train
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="_grayImage2">imageROI sau khi thresholdbinary</param>
        /// <param name="_hsvImage">blank image với chiều dài và rộng như imageROI</param>
        /// <param name="_hsvImage1">blank image với chiều dài và rộng như imageROI</param>
        /// <param name="blister">1</param>
        /// <param name="newtoado">Rectangle[] rect1 = new Rectangle[3];</param>
        /// <param name="rect">hình chữ nhật có tọa độ (0,0), chiều dài, rộng như imageROI</param>
        /// <param name="rectROI">hình chữ nhật có tọa độ (0,0), chiều dài, rộng như imageROI</param>
        public void DrawContourEllipse(Image<Gray, Byte> _grayImage2, Image<Hsv, Byte> _hsvImage,
             out Image<Hsv, Byte> _hsvImage1, int blister, out Rectangle[] newtoado, Rectangle rect, Rectangle rectROI) // rect và rectROI là như nhau
        {
            int num = 0;
            newtoado = new Rectangle[blister + 1];
            _hsvImage1 = _hsvImage.Copy();

            
            using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
                for (
                    Contour<Point> _contours = _grayImage2.FindContours(
                        Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_LINK_RUNS, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE);
                    _contours != null;
                    _contours = _contours.HNext)
                {

                    
                    if (_contours.BoundingRectangle.X >= rectROI.X && _contours.BoundingRectangle.Y >= rectROI.Y)
                    {
                        if (_contours.BoundingRectangle.X <= rectROI.X + rectROI.Width &&
                            _contours.BoundingRectangle.Y <= rectROI.Y + rectROI.Height)
                        {
                            Contour<Point> currentContour = _contours.ApproxPoly(_contours.Perimeter * 0.05, storage);
                            if (_contours.Area > rect.Width * rect.Height / 10 && _contours.Area < rect.Width * rect.Height)
                            // nếu diện tích _contour > 10% diện tích ImageROI và bé hơn 100% diện tích ImageROI)
                            {
                                if (currentContour.Total > 3 && currentContour.Total < 6)
                                //The contour has 4 or 5 vertices, it maybe a rectangle
                                {
                                    #region determine if all the angles in the contour are within the range of [80, 100] degree

                                    bool isRectangle = true;
                                    Point[] pts = currentContour.ToArray();
                                    LineSegment2D[] edges = PointCollection.PolyLine(pts, true);

                                    for (int i = 0; i < edges.Length; i++)
                                    {
                                        double angle =
                                            Math.Abs(edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i]));
                                        if (angle < 80 || angle > 100)
                                        {
                                            isRectangle = false;
                                            break;
                                        }
                                    }

                                    #endregion

                                    if (isRectangle == false)       // nếu không phải là hình chữ nhật
                                    {

                                        // hsv_img.Draw(contours.BoundingRectangle, new Hsv(0, 255, 255), 3);
                                        if (
                                            Math.Max(_contours.BoundingRectangle.Width,
                                                _contours.BoundingRectangle.Height) >
                                            Math.Min(_contours.BoundingRectangle.Width,
                                                _contours.BoundingRectangle.Height) * 11 / 10)      
                                            // nếu cạnh dài hơn lớn hơn 1,1 lần cạnh bé hơn --> cũng không phải hình tròn --> là hình ellipse
                                        {
                                            MCvBox2D newbox = new MCvBox2D();
                                            newbox = _contours.GetMinAreaRect();
                                            if (newbox.size.Width < newbox.size.Height)
                                            {
                                                newbox.angle = 90;
                                            }
                                            else
                                            {
                                                newbox.angle = 0;
                                            }
                                            Ellipse elip1 = new Ellipse(newbox);

                                            //_hsvImage1.Draw(elip1, new Hsv(0, 255, 255), 3);
                                            _hsvImage1.Draw(_contours.BoundingRectangle, new Hsv(0, 0, 0), 3);
                                            try
                                            {
                                                newtoado[num + 1].X = _contours.BoundingRectangle.X;
                                                newtoado[num + 1].Y = _contours.BoundingRectangle.Y;
                                                newtoado[num + 1].Width = _contours.BoundingRectangle.Width;
                                                newtoado[num + 1].Height = _contours.BoundingRectangle.Height;
                                                num++;
                                            }
                                            catch (Exception)
                                            {
                                                return;
                                            }
                                           
                                        }

                                    }

                                }
                            }
                        }
                        //   imageBox2.Image = _hsvImage1;
                        
                    }
                }
        }

        /// <summary>
        /// Hàm này vẽ biên, tính số đỉnh biên, tìm góc 90 độ.
        /// </summary>
        /// <param name="_grayImage2"></param>
        /// <param name="_hsvImage"></param>
        /// <param name="_hsvImage1"></param>
        /// <param name="blister"></param>
        /// <param name="newtoado"></param>
        /// <param name="rect"></param>
        /// <param name="rectROI"></param>
        public void DrawContourSquare(Image<Gray, Byte> _grayImage2, Image<Hsv, Byte> _hsvImage,
             out Image<Hsv, Byte> _hsvImage1, int blister, out Rectangle[] newtoado, Rectangle rect, Rectangle rectROI)
        {
            int num = 0;
            newtoado = new Rectangle[blister + 1];
            _hsvImage1 = _hsvImage.Copy();


            using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
                for (
                    Contour<Point> _contours = _grayImage2.FindContours(
                        Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_LINK_RUNS, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE);
                    _contours != null;
                    _contours = _contours.HNext)
                {

                    if (_contours.BoundingRectangle.X >= rectROI.X && _contours.BoundingRectangle.Y >= rectROI.Y)
                    {
                        if (_contours.BoundingRectangle.X <= rectROI.X + rectROI.Width &&
                            _contours.BoundingRectangle.Y <= rectROI.Y + rectROI.Height)
                        {
                            Contour<Point> currentContour = _contours.ApproxPoly(_contours.Perimeter * 0.05, storage);
                            if (_contours.Area > rect.Width * rect.Height / 10 && _contours.Area < rect.Width * rect.Height)
                            // nếu diện tích _contour > 10% diện tích ImageROI và bé hơn 100% diện tích ImageROI)
                            {
                                if (currentContour.Total > 3 && currentContour.Total < 6)
                                //The contour has 4 or 5 vertices, it maybe a rectangle
                                {
                                    #region determine if all the angles in the contour are within the range of [80, 100] degree

                                    bool isRectangle = true;
                                    Point[] pts = currentContour.ToArray();
                                    LineSegment2D[] edges = PointCollection.PolyLine(pts, true);

                                    for (int i = 0; i < edges.Length; i++)
                                    {
                                        double angle =
                                            Math.Abs(edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i]));
                                        if (angle < 80 || angle > 100)
                                        {
                                            isRectangle = false;
                                            break;
                                        }
                                    }

                                    #endregion

                                    if (isRectangle == true)       // nếu là hình chữ nhật
                                    {

                                        // hsv_img.Draw(contours.BoundingRectangle, new Hsv(0, 255, 255), 3);
                                        if (
                                            Math.Max(_contours.BoundingRectangle.Width,
                                                _contours.BoundingRectangle.Height) <
                                            Math.Min(_contours.BoundingRectangle.Width,
                                                _contours.BoundingRectangle.Height) * 12 / 10)
                                        // nếu cạnh dài hơn bé hơn 1,2 lần cạnh bé hơn --> vuông
                                        {
                                            //MCvBox2D newbox = new MCvBox2D();
                                            //newbox = _contours.GetMinAreaRect();
                                            //if (newbox.size.Width < newbox.size.Height)
                                            //{
                                            //    newbox.angle = 90;
                                            //}
                                            //else
                                            //{
                                            //    newbox.angle = 0;
                                            //}
                                            //Ellipse elip1 = new Ellipse(newbox);

                                            //_hsvImage1.Draw(elip1, new Hsv(0, 255, 255), 3);
                                            _hsvImage1.Draw(_contours.BoundingRectangle, new Hsv(0, 0, 0), 3);      // vẽ đường bao cho tất cả các viên thuốc
                                            
                                            try
                                            {
                                                newtoado[num + 1].X = _contours.BoundingRectangle.X;
                                                newtoado[num + 1].Y = _contours.BoundingRectangle.Y;
                                                newtoado[num + 1].Width = _contours.BoundingRectangle.Width;
                                                newtoado[num + 1].Height = _contours.BoundingRectangle.Height;
                                                num++;
                                            }
                                            catch (Exception)
                                            {
                                                return;
                                            }

                                        }

                                    }

                                }
                            }
                        }
                    }
                }
        }

        public void DrawContourCapsule(Image<Gray, Byte> _grayImage2, Image<Hsv, Byte> _hsvImage,
             out Image<Hsv, Byte> _hsvImage1, int blister, out Rectangle[] newtoado, Rectangle rect, Rectangle rectROI)
        {
            int num = 0;
            newtoado = new Rectangle[blister + 1];
            _hsvImage1 = _hsvImage.Copy();


            using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
                for (
                    Contour<Point> _contours = _grayImage2.FindContours(
                        Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_LINK_RUNS, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE);
                    _contours != null;
                    _contours = _contours.HNext)
                {
                    // kiểm tra xem contour có lọt vào trong recROI hay không
                    if (_contours.BoundingRectangle.X >= rectROI.X && _contours.BoundingRectangle.Y >= rectROI.Y)
                    {
                        if (_contours.BoundingRectangle.X <= rectROI.X + rectROI.Width &&
                            _contours.BoundingRectangle.Y <= rectROI.Y + rectROI.Height)
                        {
                            Contour<Point> currentContour = _contours.ApproxPoly(_contours.Perimeter * 0.05, storage);
                            if (_contours.Area > rect.Width * rect.Height / 10 && _contours.Area < rect.Width * rect.Height)
                            // nếu diện tích _contour > 10% diện tích ImageROI và bé hơn 100% diện tích ImageROI)
                            {
                                if (currentContour.Total > 3 && currentContour.Total < 6)
                                //The contour has 4 or 5 vertices, it maybe a rectangle
                                {
                                    #region determine if all the angles in the contour are within the range of [80, 100] degree

                                    bool isRectangle = true;
                                    Point[] pts = currentContour.ToArray();
                                    LineSegment2D[] edges = PointCollection.PolyLine(pts, true);

                                    for (int i = 0; i < edges.Length; i++)
                                    {
                                        double angle =
                                            Math.Abs(edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i]));
                                        if (angle < 80 || angle > 100)
                                        {
                                            isRectangle = false;
                                            break;
                                        }
                                    }

                                    #endregion

                                    if (isRectangle == false)       // nếu không phải là hình chữ nhật
                                    {

                                        if (
                                            Math.Max(_contours.BoundingRectangle.Width,
                                                _contours.BoundingRectangle.Height) >
                                            Math.Min(_contours.BoundingRectangle.Width,
                                                _contours.BoundingRectangle.Height) * 11 / 10)
                                        // nếu cạnh dài hơn lớn hơn 1,1 lần cạnh bé hơn --> cũng không phải hình tròn --> là hình ellipse (con nhộng cũng có hình ellipse)
                                        {
 
                                            _hsvImage1.Draw(_contours.BoundingRectangle, new Hsv(0, 0, 0), 3);
                                            try
                                            {
                                                newtoado[num + 1].X = _contours.BoundingRectangle.X;
                                                newtoado[num + 1].Y = _contours.BoundingRectangle.Y;
                                                newtoado[num + 1].Width = _contours.BoundingRectangle.Width;
                                                newtoado[num + 1].Height = _contours.BoundingRectangle.Height;
                                                num++;
                                            }
                                            catch (Exception)
                                            {
                                                return;
                                            }

                                        }

                                    }

                                }
                            }
                        }
                    }
                }
        }
        public void DrawContourHalfCapsule(Image<Gray, Byte> _grayImage2, Image<Hsv, Byte> _hsvImage,
           out Image<Hsv, Byte> _hsvImage1, int blister, out Rectangle[] newtoado, Rectangle rect, Rectangle rectROI)
        {
            int num = 0;
            newtoado = new Rectangle[blister + 1];
            _hsvImage1 = _hsvImage.Copy();


            using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
                for (
                    Contour<Point> _contours = _grayImage2.FindContours(
                        Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_LINK_RUNS, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE);
                    _contours != null;
                    _contours = _contours.HNext)
                {
                    // kiểm tra xem contour có lọt vào trong recROI hay không
                    if (_contours.BoundingRectangle.X >= rectROI.X && _contours.BoundingRectangle.Y >= rectROI.Y)
                    {
                        if (_contours.BoundingRectangle.X <= rectROI.X + rectROI.Width &&
                            _contours.BoundingRectangle.Y <= rectROI.Y + rectROI.Height)
                        {
                            Contour<Point> currentContour = _contours.ApproxPoly(_contours.Perimeter * 0.05, storage);
                            if (_contours.Area > rect.Width * rect.Height / 10 && _contours.Area < rect.Width * rect.Height)
                            // nếu diện tích _contour > 10% diện tích ImageROI và bé hơn 100% diện tích ImageROI)
                            {
                                if (currentContour.Total > 3 && currentContour.Total < 10)
                                //The contour has 4 or 5 vertices, it maybe a rectangle
                                {
                                   
                                    _hsvImage1.Draw(_contours.BoundingRectangle, new Hsv(0, 0, 0), 3);
                                    try
                                    {
                                        newtoado[num + 1].X = _contours.BoundingRectangle.X;
                                        newtoado[num + 1].Y = _contours.BoundingRectangle.Y;
                                        newtoado[num + 1].Width = _contours.BoundingRectangle.Width;
                                        newtoado[num + 1].Height = _contours.BoundingRectangle.Height;
                                        num++;
                                    }
                                    catch (Exception)
                                    {
                                        return;
                                    }

                                }

                            }

                        }
                    }
                }
        }

        /// <summary>
                     /// Hàm này sắp xếp lại tọa độ của các viên thuốc trong ảnh theo thứ tự từ trên xuống và từ trái qua
                     /// </summary>
                     /// <param name="toado"> mảng chứa tọa độ các viên thuốc </param>
                     /// <param name="num"> số lượng viên thuốc </param>
                     /// <param name="_circles">biến chứa giá trị bán kính viên thuốc, dùng để xác định khoảng cách giữa các viên thuốc</param>

        public void Arrange_CoOrdinate(Rectangle[] toado, int num, int distance_min, int distance_max)
        {
            int temp;
            int distance_width, distance_height;
            if (toado[1].Width < toado[1].Height)
            {
                distance_width = distance_min;
                distance_height = distance_max;
            }
            else
            {
                distance_width = distance_max;
                distance_height = distance_min;
            }
            for (int j = 1; j <= num; j++)
            {
                for (int i = 1; i <= num - 1; i++)
                {
                    if (toado[i].X > toado[i + 1].X + distance_width)
                    {
                        temp = toado[i].X;
                        toado[i].X = toado[i + 1].X;
                        toado[i + 1].X = temp;
                        temp = toado[i].Y;
                        toado[i].Y = toado[i + 1].Y;
                        toado[i + 1].Y = temp;
                        temp = toado[i].Width;
                        toado[i].Width = toado[i + 1].Width;
                        toado[i + 1].Width = temp;
                        temp = toado[i].Height;
                        toado[i].Height = toado[i + 1].Height;
                        toado[i + 1].Height = temp;
                    }
                    else if ((toado[i + 1].X - distance_width < toado[i].X && toado[i].X < toado[i + 1].X + distance_width))
                    {
                        if (toado[i].Y > toado[i + 1].Y + distance_height )
                        {
                            temp = toado[i].X;
                            toado[i].X = toado[i + 1].X;
                            toado[i + 1].X = temp;
                            temp = toado[i].Y;
                            toado[i].Y = toado[i + 1].Y;
                            toado[i + 1].Y = temp;
                            temp = toado[i].Width;
                            toado[i].Width = toado[i + 1].Width;
                            toado[i + 1].Width = temp;
                            temp = toado[i].Height;
                            toado[i].Height = toado[i + 1].Height;
                            toado[i + 1].Height = temp;
                        }
                    }
                }
            }
            //for (int l = 1; l <= num; l++)
            //{
            //    toado[l].X = toado[l].X - toado[l].Width/4;
            //    toado[l].Y = toado[l].Y - toado[l].Height / 4;
            //    toado[l].Width = toado[l].Width* 3/2;
            //    toado[l].Height =  toado[l].Height *3/2;

            //}
        }

       /// <summary>
        /// hàm dùng để tính giá trị trung bình của màu sắc các điểm ảnh trong viên thuốc (dùng cho viên thuốc hình tròn, hình khác cũng ko sao)
       /// </summary>
        /// <param name="gray"> ảnh ROI dùng để tính (channel 1/2/3) </param>
        /// <param name="circle">tọa độ hình tròn biểu thị cho viên thuốc mà chương trình nhận dạng được </param>
        /// <param name="sum"> tổng giá trị màu sắc tất cả các viên thuốc </param>
        /// <param name="count"> tổng số điểm ảnh các viên thuốc </param>
        /// <param name="average"> giá trị trung bình của 1 điểm ảnh trong viên thuốc </param>
        public void FindAverageOfColor (Image<Gray, Byte> gray, CircleF circle, out int average)
           
        {
           /* xét tất cả các pixel trong ROI, pixel nào có khoảng cách tới tâm hình tròn bé hơn 0.5 lần bán kính hình tròn thì cộng thêm
            mức xám của pixel đó vào biến sum, đồng thời tăng count lên 1. cuối cùng, average = sum/count */ 

           int sum = 0;
           int  count = 0;
            double khoangcach = 0;
            for (int i = 0; i < gray.Width; i++)
            {
                for (int j = 0; j < gray.Height; j++)
                {
                    PointF point = new PointF(i, j);
                    khoangcach =
                        Math.Sqrt((point.X - circle.Center.X) * (point.X - circle.Center.X) +
                                  (point.Y - circle.Center.Y) * (point.Y - circle.Center.Y));
                    if (khoangcach < 0.5 * (double)circle.Radius)      
                    {
                        sum = sum + gray.Data[j, i, 0];
                        count++;
                    }
                }
            }
            average = sum / count;
            
        }
      
    }
}
